#pragma once

#include <boost/mpi.hpp>
#include <boost/date_time/posix_time/time_serialize.hpp>

/**
  *  Boost MPI
  */
  
namespace mpi = boost::mpi;

#define MPI_RANK (p_world->rank())
#define MPI_SIZE (p_world->size())
#define MPI_ISEND p_world->isend
#define MPI_IRECV p_world->irecv
#define MPI_ISEND_VERBOSE(req, dest, tag, var, vm, m) { req = MPI_ISEND(dest, tag, var); \
  MSG_VERBOSE(vm, "n" << MPI_RANK << " send n" << dest << " : " << m); }
#define MSG_MASTER(r,m) { if (MPI_RANK == r) MSG(m); }
#define MPI_IRECV_VERBOSE(req, src, tag, var, vm, m) { req = MPI_IRECV(src, tag, var); \
  MSG_VERBOSE(vm, "n" << MPI_RANK << " wait n" << src  << " : " << m); }
#define FATAL_MASTER(r,m) { if (MPI_RANK == r) { MSG(m); exit(1); } }

namespace boost {
namespace serialization {
  static int mat_id = 0;

template<typename A>
void serialize(A& a, arma::mat& M, const unsigned int version)
{
  if (A::is_saving::value) {
    int m = M.n_rows, n = M.n_cols;
    mat_id++;
    a & mat_id;
    a & m;
    a & n;
    //MSG("save r=" << m << " c=" << n << " r=" << MPI_RANK << " id=" << mat_id);
    for (int i = 0; i < m; i++) 
      for (int j = 0; j < n; j++) {
        double v_i = M(i,j); 
        a & v_i; 
      }
  }
  else if (A::is_loading::value) {
    int m, n, save_id;
    a & save_id;
    a & m;
    a & n;
    //MSG("load r=" << m << " c=" << n << " r=" << MPI_RANK << " id=" << save_id);
    M.set_size(m, n);
    for (int i = 0; i < m; i++) 
      for (int j = 0; j < n; j++) {
        double v_i;
        a & v_i;
        M(i,j) = v_i;
      }
  }
}

} // namespace serialization
} // namespace boost

namespace boost { namespace mpi {

  template<>
  struct is_commutative<std::plus<arma::mat>, arma::mat>
    : mpl::true_ { };

} } // end namespace boost::mpi

